/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com */

/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef GLUT_WINDOW_H_
#define GLUT_WINDOW_H_

#ifdef WIN32
#include <windows.h>
#endif
#include <glut.h>
#include <assert.h>

#include "base/logging.h"
#include "base/utils.h"

#include "context.h"
#include "glut_common.h"


inline void InitGlut(int argc, char *argv[]){
	glutInit(&argc, argv);
}

static void glut_resize_callback(int width, int height){
	assert(tri::glut::glut_common::glut_context!=NULL);
	tri::glut::glut_common::glut_context->resize(width,height);
}

static void glut_display_callback(void){
	assert(tri::glut::glut_common::glut_context!=NULL);
	tri::glut::glut_common::glut_context->display();
	glutSwapBuffers();
	//float sleep_time = 1.0/60.0;
//	Sleep(sleep_time*1000);
}

static void glut_key_callback(unsigned char key, int x, int y){
	assert(tri::glut::glut_common::glut_context!=NULL);
	tri::glut::glut_common::glut_context->keyDown(key);
}

static void glut_key_up_callback(unsigned char key, int x, int y){
	assert(tri::glut::glut_common::glut_context!=NULL);
	tri::glut::glut_common::glut_context->keyUp(key);
}

static void glut_specail_key_callback(int key, int x, int y){
	assert(tri::glut::glut_common::glut_context!=NULL);
	int skey = -1;
	switch(key){
	case GLUT_KEY_LEFT:skey = tri::KEY_LEFT;break;
	case GLUT_KEY_RIGHT:skey = tri::KEY_RIGHT;break;
	case GLUT_KEY_UP:skey = tri::KEY_UP;break;
	case GLUT_KEY_DOWN:skey = tri::KEY_DOWN;break;
	}
	
	if (skey!=-1){
		tri::glut::glut_common::glut_context->keyDown(skey);
	}
}

static void glut_specail_key_up_callback(int key, int x, int y){
	assert(tri::glut::glut_common::glut_context!=NULL);
	int skey = -1;
	switch(key){
	case GLUT_KEY_LEFT:skey = tri::KEY_LEFT;break;
	case GLUT_KEY_RIGHT:skey = tri::KEY_RIGHT;break;
	case GLUT_KEY_UP:skey = tri::KEY_UP;break;
	case GLUT_KEY_DOWN:skey = tri::KEY_DOWN;break;
	}
	
	if (skey!=-1){
		tri::glut::glut_common::glut_context->keyUp(skey);
	}
}

static void glut_idle_callback(void){
	assert(tri::glut::glut_common::glut_context!=NULL);
	glutPostRedisplay();
}

static void glut_mouse_callback(int button, int state, int x, int y){
	assert(tri::glut::glut_common::glut_context!=NULL);
	
	int _button = tri::NONE;
	switch(button){
	case GLUT_LEFT_BUTTON:
		_button = tri::BUTTON_LEFT;
		break;
	case GLUT_MIDDLE_BUTTON:
		_button = tri::BUTTON_MIDDLE;
		break;
	case GLUT_RIGHT_BUTTON:
		_button = tri::BUTTON_RIGHT;
		break;
	};
	
	switch(state){
	case GLUT_DOWN:
		tri::glut::glut_common::glut_context->mouseDown(x,y,button);
		break;
	case GLUT_UP:
		tri::glut::glut_common::glut_context->mouseUp(x,y,button);
		break;
	}	
}

static void glut_mouse_motion_callback(int x, int y){
	assert(tri::glut::glut_common::glut_context!=NULL);
	int _x,_y;
	tri::glut::glut_common::glut_context->getLastMousePosition(_x,_y);
	tri::glut::glut_common::glut_context->setLastMousePosition(x,y);
	tri::glut::glut_common::glut_context->mouseDrag(x,y,x-_x,y-_y,tri::glut::glut_common::glut_context->getLastButtonPressed());
}

static void glut_passive_mouse_motion_callback(int x, int y){
	assert(tri::glut::glut_common::glut_context!=NULL);
	assert(tri::glut::glut_common::glut_context!=NULL);
	int _x,_y;
	tri::glut::glut_common::glut_context->getLastMousePosition(_x,_y);
	tri::glut::glut_common::glut_context->setLastMousePosition(x,y);
	tri::glut::glut_common::glut_context->mouseMove(x,y,x-_x,y-_y);
}

static void show_glut_window(int width,int height,std::string caption="Graphics Window",bool fullscreen = false){

	//InitGlut(0,NULL);
	
    glutInitWindowPosition(10,10);
    glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
	glutInitWindowSize(width,height);
	glutCreateWindow(caption.c_str());
	
	if(fullscreen){
		glutFullScreen();
	}
    
    glutReshapeFunc(glut_resize_callback);
    glutDisplayFunc(glut_display_callback);
    glutKeyboardFunc(glut_key_callback);
    glutKeyboardUpFunc(glut_key_up_callback);
    glutSpecialFunc(glut_specail_key_callback);
    glutSpecialUpFunc(glut_specail_key_up_callback);
    glutIdleFunc(glut_idle_callback);
    
    glutMouseFunc(glut_mouse_callback);
    glutMotionFunc(glut_mouse_motion_callback);
    glutPassiveMotionFunc(glut_passive_mouse_motion_callback);
	tri::glut::glut_common::glut_context->init();
    glutMainLoop();
}

namespace tri{
namespace glut{

class TGlutWindow:public tri::TWindow{
public:
	TGlutWindow():tri::TWindow(){};
	virtual ~TGlutWindow(){};
	
protected:
	virtual void start(int width,int height,std::string caption="Graphics Window",bool fullscreen = false){
		
		LOG_INFO( "Starting GLUT Window " + caption + " FullScreen=" + tri::utils::int2String(fullscreen) + 
				" width=" + tri::utils::int2String(width) + " height=" + tri::utils::int2String(height) );
		
		assert(tri::glut::glut_common::glut_context == NULL);
		assert(_context != NULL);
		tri::glut::glut_common::glut_context = _context;
		show_glut_window(width,height,caption,fullscreen);
	}

};
}/* NAMESPACE GLUT */


}
#endif
